#include <dcont/dprompt.h>
static TLS_INITIAL prompt_t *cur_prompt;
static TLS_INITIAL presume_t root_prompt;
DEF_PROMPT_MODULE(cur_prompt, (prompt_t *)&root_prompt)

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

word_t test_val = 0;
PROMPT_FUNC NOINLINE word_t prompt_test1(prompt_t *p, word_t arg){
	
	assert(prompt_cur() == p);
	prompt_t *c = (prompt_t *)arg;
	prompt_return_v(c, test_val++);
}

void test_resume(){
	void *stk = malloc(8192);
	assert(stk);
	
	prompt_t *c = prompt_init(stk, 8192);
	fprintf(stderr, "test prompt malloc stk %p, prompt %p", stk, (void *)c);
	word_t p;
	assert(prompt_cur() == (prompt_t *)&root_prompt);
	p = prompt_exec(c, prompt_test1, (word_t)c);
	assert(p == 0);
	p = prompt_exec(c, prompt_test1, (word_t)c);
	assert(p == 1);
	free(stk);
	assert(prompt_cur() == (prompt_t *)&root_prompt);
}

fresume_t buf;
PROMPT_FUNC word_t cont_test1(word_t arg){
	printf("cont_test1 running...%p\n", (void *)arg);
	LONGJMP(&buf, 1);
}

void test_fastcont(){
	void *stk = aligned_alloc(4096, 8192);
	assert(stk);
	if (0 == SETJMP(&buf)){
		setstack_exec(cont_test1, 1, (paddr_t)stk + 8192 - 16);
	}
	free(stk);
}



PROMPT_FUNC word_t prompt_test2(prompt_t *p, word_t arg){
	PARAM_USED(arg);
	// word_t ret;
	prompt_t *c = p;
	// printf("%p, %p\n", c, prompt_cur());
	assert(c == prompt_cur());
	word_t yval = _prompt_yieldf_val(c, NULL_PTR, 0, 0);
	// printf("%p, %p, ready to return\n", c, prompt_cur());
	assert(c == prompt_cur());
	prompt_return_v(c, yval + 1);

	// yval = prompt_yield(my);
	// printf("get yield val %llu\n", yval);
	// prompt_return(my, yval * 2);
}

void test_yieldresume(){
	assert(prompt_cur() == (prompt_t *)&root_prompt);
	void *stk = aligned_alloc(4096, 8192);
	assert(stk);
	prompt_t *cyf = prompt_init(stk, 8192);
	// printf("test prompt malloc stk %p, prompt %p, stkptr %p\n", stk, c, c->stk_ptr);
	word_t p = prompt_exec(cyf, prompt_test1, (word_t)cyf);
	assert(p == 2);

	prompt_exec(cyf, prompt_test2, (word_t)cyf);
	// assert(p == (word_t)cyf);
	assert(prompt_cur() == (prompt_t *)&root_prompt);
	printf("prompt yield, ready to resume\n");
	word_t t;
	
	/* iterator */
	t = prompt_resume_v(cyf, 42);
	printf("prompt return %lu\n", (ulong_t)t);
	assert(t == 43);
	assert(prompt_cur() == (prompt_t *)&root_prompt);

	t = prompt_resume_v(cyf, 99);
	printf("prompt return %lu\n", (ulong_t)t);
	assert(t == 100);
	assert(prompt_cur() == (prompt_t *)&root_prompt);
	free(stk);
}



typedef void test_iter_func_t(word_t iter_num);
static inline void test_iter_func(word_t iter_num){
	printf("%lu, ", (ulong_t)iter_num);
}

static word_t prompt_test3_iter(word_t arg){
	word_t idx_max = arg;
	prompt_t *my = prompt_cur();
	test_iter_func_t *p = (test_iter_func_t *)prompt_yield(my);
	for (word_t idx = 0; idx < idx_max; idx++){
		p(idx);
	}
	prompt_return_v(my, idx_max);
}

DEF_PROMPT_FUNC_RETV(prompt_test3_iter)

void test_iter(){
	void *stk = aligned_alloc(4096, 8192);
	assert(stk);
	prompt_t *itr = prompt_init(stk, 8192);

	prompt_exec(itr, PROMPT_BYFUNC(prompt_test3_iter), (word_t)15);
	// assert(p == (word_t)itr);
	word_t ret = prompt_resume_v(itr, (word_t)test_iter_func);
	assert(ret == 15);
	free(stk);
}

#define MAX_PROMPT (65536)
prompt_t *ptarr[MAX_PROMPT];
PROMPT_FUNC word_t prompt_test4(prompt_t *p, word_t arg){
	word_t ret;
	prompt_t *c = p;
	assert(prompt_cur() == c);
	
	
	if (arg < MAX_PROMPT - 1){
		word_t next = arg + 1;
		// printf("[prompt %llu] ready to create\n", (ullong_t)arg);
		ret = prompt_exec(ptarr[next], prompt_test4, next);
	}else{
		word_t val = prompt_yield(ptarr[0]);
		printf("[nested %llu]get yield ptr %llx\n", (ullong_t)arg, (ullong_t)val);
		ret = val;
	}
	prompt_return_v(c, ret);

}

void test_nested(){

	for(word_t i = 0; i < MAX_PROMPT; i++){
		void *stk = aligned_alloc(4096, 8192);
		assert(stk);
		ptarr[i] = prompt_init(stk, 8192);
	}
	
	prompt_exec(ptarr[0], prompt_test4, (word_t)0);
	// assert(p == (word_t)itr);
	printf("nested "TO_STRING(MAX_PROMPT)" yield back\n");
	assert(prompt_cur() == prompt_root());
	word_t ret = prompt_resume_v(ptarr[0], (word_t)test_iter_func);
	assert(ret == (word_t)test_iter_func);

}

#define prompt_lambda(body) ({ word_t __lambda_func__ (prompt_t *p, word_t arg) \
	body \
	__lambda_func__;})


/* lambda 写法 */
void test_lambda(){
#ifndef __clang__
	int f(int y){
		return y + 1;
	}
	promptf_t *p = prompt_lambda({ PARAM_USED(arg); int a= 1; int b = 2; prompt_return_v(p, a+b); });
	PARAM_USED(p, f);
#else

#endif
}

int main(){
	

	test_fastcont();
	test_resume();

	test_yieldresume();
	test_iter();
	test_nested();
	test_lambda();
	printf("all test passed\n");
	return 0;
}
